/*
 * Copyright (c) 2008, Xiong Yingfei, the University of Tokyo All rights
 * reserved.
 */
package ac.jp.u_tokyo.SyncLib.dictionaries;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import ac.jp.u_tokyo.SyncLib.Mod;
import ac.jp.u_tokyo.SyncLib.NullMod;
import ac.jp.u_tokyo.SyncLib.PrimMod;
import ac.jp.u_tokyo.SyncLib.util.Helper;

public class DictMod<K, V> implements Mod<Map<K, V>>,
		Iterable<Entry<K, Mod<V>>>
{

//	public static class InnerMod<Key, Const>
//	{
//		private Key _key;
//		private Mod<Const> _mod;
//
//		public Key getKey()
//		{
//			return _key;
//		}
//
//		public InnerMod(Key key, Mod<Const> mod)
//		{
//			super();
//			_key = key;
//			_mod = mod;
//		}
//
//		void apply(ForMap<Key, Const> map) {
//			Const value = _mod.apply(map.get(_key));
//			if (value == null)
//				map.remove(_key);
//		}
//
//		void inPlaceApply(ForMap<Key, Const> map) {
//			Const value = _mod.inPlaceApply(map.get(_key));
//			if (value == null)
//				map.remove(_key);
//
//		}
//
//		public boolean isConflict(InnerMod<Key, Const> mod) {
//			return _mod.isConflict(mod._mod);
//		}
//
//		public InnerMod<Key, Const> getInverse(ForMap<Key, Const> map){
//			return new InnerMod<Key, Const>(_key, _mod.getInverse(map.get(_key)));
//		}
//
//		@Override
//		public int hashCode()
//		{
//			final int prime = 31;
//			int result = 1;
//			result = prime * result + ((_key == null) ? 0 : _key.hashCode());
//			return result;
//		}
//
//		@Override
//		public boolean equals(Object obj)
//		{
//			if (this == obj) return true;
//			if (obj == null) return false;
//			if (getClass() != obj.getClass()) return false;
//			final InnerMod other = (InnerMod) obj;
//			if (_key == null)
//			{
//				if (other._key != null) return false;
//			}
//			else if (!_key.equals(other._key)) return false;
//			return true;
//		}
//		
//		
//
//	}

//	@Deprecated
//	public static class PutMod<Key, Const> extends
//			InnerMod<Key, Const>
//	{
//		private Mod<Const> _innerModification;
//
//		public PutMod(Key key, Mod<Const> value)
//		{
//			super(key);
//			assert value != null;
//			_innerModification = value;
//		}
//
//		public Mod<Const> getModifcation()
//		{
//			return _innerModification;
//		}
//
//		@Override
//		void apply(ForMap<Key, Const> map)
//		{
//			map.put(getKey(), getModifcation().apply(map.get(getKey())));
//		}
//
//		@SuppressWarnings("unchecked")
//		public boolean isConflict(InnerMod<Key, Const> mod)
//		{
//			if (mod instanceof PutMod)
//			{
//				return ((PutMod<Key, Const>) mod)._innerModification
//						.isConflict(_innerModification);
//			}
//			return false;
//		}
//
//		@Override
//		public InnerMod<Key, Const> getInverse(ForMap<Key, Const> map)
//		{
//			assert map != null;
//			if (map.containsKey(getKey()))
//				return new PutMod<Key, Const>(getKey(),
//						_innerModification.getInverse(map.get(getKey())));
//			else
//				return new RemoveMod<Key, Const>(getKey());
//		}
//
//		@Override
//		void inPlaceApply(ForMap<Key, Const> map)
//		{
//			Const value = map.get(getKey());
//			Const mValue = _innerModification.inPlaceApply(value);
//			if (mValue != value) map.put(getKey(), mValue);
//		}
//
//		@Override
//		public int hashCode()
//		{
//			final int prime = 31;
//			int result = super.hashCode();
//			result = prime
//					* result
//					+ ((_innerModification == null) ? 0 : _innerModification
//							.hashCode());
//			return result;
//		}
//
//		@Override
//		public boolean equals(Object obj)
//		{
//			if (this == obj) return true;
//			if (!super.equals(obj)) return false;
//			if (getClass() != obj.getClass()) return false;
//			final PutMod other = (PutMod) obj;
//			if (_innerModification == null)
//			{
//				if (other._innerModification != null) return false;
//			}
//			else if (!_innerModification.equals(other._innerModification))
//				return false;
//			return true;
//		}
//
//		@Override
//		public String toString()
//		{
//			return "Put(" + getKey() + ", " + _innerModification.toString() + ")";
//		}
//		
//		
//
//	}
//
//	@Deprecated
//	public static class RemoveMod<Key, Const> extends
//			InnerMod<Key, Const>
//	{
//
//		public RemoveMod(Key key)
//		{
//			super(key);
//		}
//
//		@Override
//		void apply(ForMap<Key, Const> map)
//		{
//			map.remove(getKey());
//
//		}
//
//		@Override
//		public boolean isConflict(InnerMod<Key, Const> mod)
//		{
//			if (mod instanceof RemoveMod) return false;
//			return true;
//		}
//
//		@Override
//		public InnerMod<Key, Const> getInverse(ForMap<Key, Const> map)
//		{
//			Const v = map.get(getKey());
//			if (v == null)
//				return this;
//			else
//				return new PutMod<Key, Const>(getKey(),
//						new PrimMod<Const>(v));
//		}
//
//		@Override
//		void inPlaceApply(ForMap<Key, Const> map)
//		{
//			apply(map);
//		}
//		
//		@Override
//		public String toString()
//		{
//			return "Remove(" + getKey() + ")";
//		}
//
//
//	}

	private Map<K, Mod<V>> _modifications = new HashMap<K, Mod<V>>();
	
	public DictMod(Map<K, Mod<V>> innerMods)
	{
		assert innerMods != null;
		for(Map.Entry<K, Mod<V>> entry : innerMods.entrySet()) {
			if (entry.getValue() != null && false == entry.getValue() instanceof NullMod) {
				_modifications.put(entry.getKey(), entry.getValue());
			}
		}
//		
//		assert innerMods.iterator().hasNext();
//		ForMap<K, InnerMod<K, V>> former = _modifications;
//		Iterable<InnerMod<K, V>> latter = innerMods;
//		merge(former, latter);
	}

//	@SuppressWarnings("unchecked")
//	private void merge(ForMap<K, InnerMod<K, V>> former,
//			Iterable<InnerMod<K, V>> latter)
//	{
//		for (InnerMod<K, V> newMod : latter)
//		{
//			InnerMod<K, V> existing = former.get(newMod.getKey());
//			InnerMod<K, V> result;
//			result = mergeSingle(newMod, existing);
//			former.put(newMod.getKey(), result);
//		}
//	}
//
//	private InnerMod<K, V> mergeSingle(InnerMod<K, V> latter,
//			InnerMod<K, V> former)
//	{
//		InnerMod<K, V> result;
//		if (former instanceof PutMod)
//			if (latter instanceof PutMod)
//			{
//				final PutMod<K, V> existingPut = (PutMod) former;
//				final PutMod<K, V> latterPut = (PutMod) latter;
//
//				result = new PutMod<K, V>(latter
//						.getKey(), existingPut._innerModification
//						.merge(latterPut._innerModification));
//			}
//			else
//				result = latter;
//		else
//			result = latter;
//		return result;
//	}

	public Iterator<Entry<K, Mod<V>>> iterator()
	{
		return _modifications.entrySet().iterator();
	}
	
	private Map<K, Mod<V>> getMods(){
		return _modifications;
	}

	public Mod<V> getMod(K k)
	{
		Mod<V> result = _modifications.get(k);
		if (result == null)
			return NullMod.INSTANCE;
		return result;
		
	}

	public int size()
	{
		return _modifications.size();
	}

	public Map<K, V> apply(Map<K, V> value)
	{
		Map<K, V> result;
		if (value != null) result = (Map<K, V>) Helper.deepCopy(value);//new HashMap<K, V>(value);
		else result = new HashMap<K, V>();
		for (Entry<K, Mod<V>> i : this)
		{
			V v = i.getValue().apply(result.get(i.getKey()));
			if (v == null)
				result.remove(i.getKey());
			else
				result.put(i.getKey(), v);
		}
		return result;
	}
	
	public Collection<K> getKeys(){
		return _modifications.keySet();
	}

	@SuppressWarnings("unchecked")
	public boolean isConflict(Mod<Map<K, V>> m)
	{
		if (m == null) return false;
		if (m instanceof NullMod) return false;
		if (!(m instanceof DictMod)) return true;
		DictMod<K, V> rm = (DictMod<K, V>) m;
		for (Entry<K, Mod<V>> inner : this)
		{
			Mod<V> rmod = rm.getMod(inner.getKey());
			if (rmod != null && rmod.isConflict(inner.getValue())) return true;
		}
		return false;
	}

	public Map<K, V> inPlaceApply(Map<K, V> value)
	{
		if (value == null) value = new HashMap<K, V>();
		for (Entry<K, Mod<V>> i : this)
		{
			V v = i.getValue().apply(value.get(i.getKey()));
			if (v == null)
				value.remove(i.getKey());
			else
				value.put(i.getKey(), v);
		}
		return value;
	}

	public Mod<Map<K, V>> getInverse(Map<K, V> value)
	{
		if (value == null) return PrimMod.NULL;
		DictModFactory<K, V> f = new DictModFactory<K, V>();
		for (Entry<K, Mod<V>> i : this)
		{
			f.addPut(i.getKey(), i.getValue().getInverse(value.get(i.getKey())));
		}
		return f.create();
	}

	@SuppressWarnings("unchecked")
	public Mod<Map<K, V>> merge(Mod<Map<K, V>> latter)
	{
		if (latter == null) return this;
		if (latter instanceof NullMod) return this;
		if (latter instanceof PrimMod) return latter;
		assert latter instanceof DictMod;
		DictMod<K, V> result = new DictMod<K, V>(this._modifications);
		for (Entry<K, Mod<V>> i : (DictMod<K, V>)latter) {
			result._modifications.put(i.getKey(), Helper.merge(result.getMod(i.getKey()), i.getValue()));
		}
		return result;
	}

	@Override
	public int hashCode()
	{
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((_modifications == null) ? 0 : _modifications.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj)
	{
		if (this == obj) return true;
		if (obj == null) return false;
		if (getClass() != obj.getClass()) return false;
		final DictMod other = (DictMod) obj;
		if (_modifications == null)
		{
			if (other._modifications != null) return false;
		}
		else if (!_modifications.equals(other._modifications)) return false;
		return true;
	}

	@Override
	public String toString()
	{
		StringBuffer result = new StringBuffer();
		result.append("{");
		for (Entry<K, Mod<V>> inner : this) {
			result.append(inner.getKey()).append("->").append(inner.getValue()).append(", ");
		}
		int end = result.length() - 2;
		if (end < 1) end = 1;
		return result.substring(0, end) + "}";
	}
	
	public static<K, V> Mod replaceNullable(Mod mod, K key, Mod<V> value){
		if (value instanceof NullMod) return mod;
		DictMod<K, V> dictMod;
		if (mod instanceof NullMod) dictMod = NULLDICTMOD;
		else
			dictMod = (DictMod<K, V>) mod;
		return replace(dictMod, key, value);

	}
	
	public static<K, V> DictMod<K, V> replace(DictMod<K, V> mod, K key, Mod<V> value){
		if (value == null) return mod;
		if (value instanceof NullMod) return mod;
		Map<K, Mod<V>> dictModMap = new HashMap(mod.getMods());
		dictModMap.put(key, value);
		return new DictMod<K, V>(dictModMap);

	}

	private static final DictMod NULLDICTMOD = new DictMod(new HashMap(0));
	public static <K, V> DictMod<K, V> nullMod2NullDictMod(Mod<Map<K, V>> mod) {
		assert mod instanceof NullMod || mod instanceof DictMod : mod.getClass().getName();
		if (mod instanceof NullMod)
			return NULLDICTMOD;
		else
			return (DictMod) mod;
	}

	public Mod<Map<K, V>> subtract(Mod<Map<K, V>> subtrahend)
	{
//		assert this.merge(subtrahend).equals(this);
		if (subtrahend instanceof NullMod) return this;
		Map<K, Mod<V>> modDict = new HashMap<K, Mod<V>>();
		for(Entry<K, Mod<V>> entry : _modifications.entrySet()) {
			modDict.put(entry.getKey(), entry.getValue().subtract(((DictMod) subtrahend).getMod(entry.getKey())));
		}
		DictMod<K, V> result = new DictMod<K, V>(modDict);
		if (result._modifications.size() == 0) return NullMod.INSTANCE;
		return result;
	}
}
